/*
	First sample of OpenGL and X Window system

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <X11/X.h>
#include <X11/keysymdef.h>
#include <X11/Xlib.h>

#define GL_GLEXT_PROTOTYPES

#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <GL/glcorearb.h>

#include "vertexes.h"
#include "shaders.h"
#include "bmp_reader.h"
#include "textures.h"
#include "matrix.h"

Display* dpy;
Window root;
GLint att[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
XVisualInfo* vi;
Colormap cmap;
XSetWindowAttributes swa;
Window win;
GLXContext glc;
XWindowAttributes gwa;
XEvent xev;

#define BOX_COUNT 20

typedef struct {
	float ms;
	float tr[3];
	float angle;
	unsigned int tex;
	float trans[16];
} MyBox;

void makeBoxTrans(MyBox* box) {
	float trans[] = {
		box->ms*cos(box->angle), -box->ms*sin(box->angle), 0.0, box->tr[0],
		box->ms*sin(box->angle), box->ms*cos(box->angle), 0.0, box->tr[1],
		0.0, 0.0, box->ms, box->tr[2],
		0.0, 0.0, 0.0, 1.0 };
		memcpy(box->trans, trans, sizeof(trans));
};

MyBox* makeBox(float ms, float tx, float ty, float tz, float angle, unsigned int tex) {
	MyBox* box = (MyBox*)malloc(sizeof(MyBox));
	box->ms = ms;
	box->tr[0] = tx;
	box->tr[1] = ty;
	box->tr[2] = tz;
	box->tex = tex;
	box->angle = angle;
	makeBoxTrans(box);
	return box;
};

void freeMyBox(MyBox* box) {
	free(box);
};

int main(int argc, char* argv[]) {

	srand(time(NULL));

	if(argc != 5) {
		printf("syntax: %s <vertex shader file> <fragment shader file> <bmp file1> <bmp file2>\n", argv[0]);
		return 0;
	}
	dpy = XOpenDisplay(NULL);
	if(dpy == NULL) {
		printf("\n\tCannot connect to X server\n\n");
		exit(0);
	};

	root = DefaultRootWindow(dpy);
	vi = glXChooseVisual(dpy, 0, att);
	if(vi == NULL) {
		printf("\n\tno appropriate visual found\n\n");
		exit(0);
	} else {
		printf("\n\tvisual %p selected\n", (void*)vi->visualid);
	};

	cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
	swa.colormap = cmap;
	swa.event_mask = KeyPressMask;

	win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
	XMapWindow(dpy, win);
	XStoreName(dpy, win, "OpenGL and Coords");

	glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
	glXMakeCurrent(dpy, win, glc);

	glEnable(GL_DEPTH_TEST);

	unsigned int shaderProgram = CreateShaderProgram(argv[1], argv[2]);

	float verts[] = {	-1.0, 1.0, 0.0, 0.0, 0.0,
										1.0, -1.0, 0.0, 1.0, 1.0,
										-1.0, -1.0, 0.0, 0.0, 1.0,
										1.0, 1.0, 0.0, 1.0, 0.0};

	unsigned int inds[] = {0,1,2, 0,3,1};

	unsigned int tex_box = loadBmpTexture(argv[3]);
	unsigned int tex_back = loadBmpTexture(argv[4]);

	glUseProgram(shaderProgram);
	glUniform1i(glGetUniformLocation(shaderProgram, "tex1"), 0);

	unsigned int uniform_trans = glGetUniformLocation(shaderProgram, "trans");

	float ms = 0.3;
	float tr[] = {0.5, 0.5, 0.0};
	float angle = 0.3;

	float trans[] = {
		ms*cos(angle), -ms*sin(angle), 0.0, tr[0],
		ms*sin(angle), ms*cos(angle), 0.0, tr[1],
		0.0, 0.0, ms, tr[2],
		0.0, 0.0, 0.0, 1.0 };

	MyBox* boxes[BOX_COUNT];
	for(int i=0; i<BOX_COUNT-1; i++) {
		float ms = (float)(rand() % 100) / 500.0 + 0.1;
		float tx = (float)(rand() % 150) / 100.0 - 0.75;
		float ty = (float)(rand() % 150) / 100.0 - 0.75;
		float tz = (float)(rand() % 150) / 100.0 - 0.75;
		float angle = (float)(rand() % 360) / 360 * 6.28;
		boxes[i] = makeBox(ms, tx, ty, tz, angle, tex_box);
	};

	boxes[BOX_COUNT-1] = makeBox(1.0, 0.0, 0.0, 0.1, 0.0, tex_back);


	glUniformMatrix4fv(uniform_trans, 1, GL_FALSE, trans);

	unsigned int VAO = CreateVAO(verts, sizeof(verts), inds, sizeof(inds));

	while(1) {

		if(XCheckTypedWindowEvent(dpy, win, KeyPress, &xev)) {
			KeySym sym = XLookupKeysym(&xev.xkey, 0);
      switch(sym) {
        case XK_Escape:
					for(int i=0; i<BOX_COUNT; i++) freeMyBox(boxes[i]);
					glXMakeCurrent(dpy, None, NULL);
					glXDestroyContext(dpy, glc);
					XDestroyWindow(dpy, win);
					XCloseDisplay(dpy);
					exit(0);
					break;
        default:
					break;
      };
    };

		XGetWindowAttributes(dpy, win, &gwa);

		glViewport(0, 0, gwa.width, gwa.height);

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		for(int i=0; i<BOX_COUNT; i++) {
			glUseProgram(shaderProgram); /* Активируем шейдерную программу */
			glUniformMatrix4fv(uniform_trans, 1, GL_FALSE, boxes[i]->trans);

			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, boxes[i]->tex);

			glBindVertexArray(VAO); /* Активируем массив с данными о вершинах */
			glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); /* Отображаем треугольник */
		};
		glXSwapBuffers(dpy, win);
	};
};
